package org.scala.javafx.rich

import javafx.collections.FXCollections
import javafx.collections.ListChangeListener
import javafx.collections.ObservableList

import scala.collection.JavaConverters._
import org.scala.javafx.rich.Enrich._

class RichObservableList[T](val self: ObservableList[T]) {
  def addChangeListener(f: ListChangeListener.Change[_ <: T] => Unit) {
    self.addListener(new ListChangeListener[T]() {
      def onChanged(change: ListChangeListener.Change[_ <: T]): Unit = f(change)
    })
  }
  
  def translateInto[U](target: ObservableList[U])(f: T => U): Unit = {
    target.clear()
    for (item <- self.asScala) {
      target.add(f(item))
    }
    this.addChangeListener { change =>
      while (change.next) {
        if (change.wasPermutated) {
          for (i <- change.getFrom to change.getTo) {
            target.set(i, f(self.get(i)))
          }
        } else {
          if (change.getRemovedSize > 0) {
            target.remove(change.getFrom, change.getFrom + change.getRemovedSize)
          }
          if (change.getFrom != change.getTo) {
            target.addAll(change.getFrom, change.getAddedSubList.asScala.map(f).asJava)
          }
        }
      }
    }
  }
}

object RichObservableList {
  def show(ints: ObservableList[Int], strings: ObservableList[String]): Unit = {
    var delim = ""
    print("ints: ")
    for (value <- ints.asScala) {
      print(delim)
      print(value)
      delim = ", "
    }
    println()
    delim = ""
    print("strings: ")
    for (value <- strings.asScala) {
      print(delim)
      print("\"" + value.replaceAll("\"", "\\\"") + "\"")
      delim = ", "
    }
    println()
  }
  
  def main(args: Array[String]): Unit = {
    val ints: ObservableList[Int] = FXCollections.observableArrayList()
    val strings: ObservableList[String] = FXCollections.observableArrayList()
    ints.translateInto(strings) { f =>
      f.toString()
    }
    println("-- ints.add(0, 0) --")
    ints.add(0, 0)
    show(ints, strings)
    println("-- ints.add(0, 1) --")
    ints.add(0, 1)
    show(ints, strings)
    println("-- ints.addAll(1, List(2,3,4).asJava) --")
    ints.addAll(1, List(2,3,4).asJava)
    show(ints, strings)
    println("-- ints.remove(1, 2) --")
    ints.remove(1, 2)
    show(ints, strings)
    println("-- ints.remove(1, 3) --")
    ints.remove(1, 3)
    show(ints, strings)
    println("-- ints.set(1, 5) --")
    ints.set(1, 5)
    show(ints, strings)
  }
}
